const debug = require('debug')('talk:services:karma');
const UserModel = require('../models/user');
const {
  TRUST_THRESHOLDS
} = require('../config');

/**
 * This will create an object with the property name of the action type as the
 * key and an object as it's value. This will contain a RELIABLE, and UNRELIABLE
 * property with the number of karma points associated with their particular
 * state.
 *
 * If only the RELIABLE variable is provided, then it will also be used as the
 * UNRELIABLE variable.
 *
 * The form of the environment variable is:
 *
 *  <name>:<RELIABLE>,<UNRELIABLE>;<name>:<RELIABLE>,<UNRELIABLE>;...
 *
 * The default used is:
 *
 *  comment:2,-2;flag:2,-2
 */
const parseThresholds = (thresholds) => thresholds
  .split(';')
  .filter((threshold) => threshold && threshold.length > 0)
  .reduce((acc, threshold) => {
    const thresholds = threshold.split(':');
    if (thresholds.length < 2) {
      return acc;
    }

    let [name, values] = thresholds;
    let [RELIABLE, UNRELIABLE] = values.split(',').map((value) => parseInt(value));

    if (!(name in acc)) {
      acc[name] = {};
    }

    if (isNaN(UNRELIABLE) && !isNaN(RELIABLE)) {
      acc[name].RELIABLE = RELIABLE;
      acc[name].UNRELIABLE = RELIABLE;
    } else {
      if (!isNaN(UNRELIABLE)) {
        acc[name].UNRELIABLE = UNRELIABLE;
      }

      if (!isNaN(RELIABLE)) {
        acc[name].RELIABLE = RELIABLE;
      }
    }

    return acc;
  }, {
    comment: {
      RELIABLE: 0,
      UNRELIABLE: 0
    },
    flag: {
      RELIABLE: 0,
      UNRELIABLE: 0
    }
  });

const THRESHOLDS = parseThresholds(TRUST_THRESHOLDS);

debug('using thresholds: ', THRESHOLDS);

/**
 * KarmaModel represents the checkable properties of a user and wrapps the
 * KarmaService function `isReliable` to work flexibly with the graph.
 */
class KarmaModel {
  constructor(model) {
    this.model = model;
  }

  get flagger() {
    return KarmaService.isReliable('flag', this.model);
  }

  get commenter() {
    return KarmaService.isReliable('comment', this.model);
  }
}

/**
 * KarmaService provides interfaces for editing a user's karma.
 */
class KarmaService {

  /**
   * Model returns a KarmaModel based on the passed in user.
   */
  static model(user) {
    if (user === null || !user.metadata || !user.metadata.trust) {
      return new KarmaModel({});
    }

    return new KarmaModel(user.metadata.trust);
  }

  /**
   * Inspects the reliability of a property and returns it if known.
   * @param {String} name - name of the property
   * @param {Object} trust - object possibly containing the propertys
   */
  static isReliable(name, trust) {
    if (trust && trust[name]) {
      if (trust[name].karma > THRESHOLDS[name].RELIABLE) {
        return true;
      } else if (trust[name].karma < THRESHOLDS[name].UNRELIABLE) {
        return false;
      }
    } else if (THRESHOLDS[name].RELIABLE < 0) {
      return true;
    } else if (THRESHOLDS[name].UNRELIABLE > 0) {
      return false;
    }

    return null;
  }

  /**
   * modifyUserKarma updates the user to adjust their karma, for either the `type`
   * of 'comment' or 'flag'. If `multi` is true, then it assumes that `id` is an
   * array of id's.
   */
  static async modifyUser(id, direction = 1, type = 'comment', multi = false) {
    const key = `metadata.trust.${type}.karma`;

    let update = {
      $inc: {
        [key]: direction
      }
    };

    if (multi) {

      // If it was in multi-mode but there was no user's to adjust, bail.
      if (id.length <= 0) {
        return;
      }

      return UserModel.update({
        id: {
          $in: id
        }
      }, update, {
        multi: true
      });
    }

    return UserModel.update({id}, update);
  }
}

module.exports = KarmaService;
